/**
 * Copyright 2007-2008 Vidar Svansson
 *     
 * This file is part of JAdapter.
 * 
 * JAdapter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JAdapter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JAdapter.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.jadapter.registry;

import org.jadapter.AdapterFactory;
import org.jadapter.Query;
import org.jadapter.beans.AdapterBean;
import org.jadapter.graph.Graph;
import org.jadapter.graph.TransitiveClosure;

public class TransitiveAdapterRegistry extends AbstractAdapterRegistry {

    private Graph<Class<?>, Query<AdapterFactory<Object,Object>>> graph;
    private Graph<Class<?>, Query<AdapterFactory<Object,Object>>> implicitGraph;
    
    public TransitiveAdapterRegistry(){
        graph = new TransitiveClosure<Class<?>, AdapterFactory<Object,Object>>();
        implicitGraph = new TransitiveClosure<Class<?>, AdapterFactory<Object,Object>>();

    }
    
    public TransitiveAdapterRegistry(AdapterFactory ... adapters) {
        this();
        this.register(adapters);
    }
    
    
    
    @SuppressWarnings("unchecked")
    public <T> T adapt(final Class<T> type, final Object context) {
        Class<?> klass = context.getClass(); 
        Query<AdapterFactory<Object,Object>> adapters = null;

        while (adapters == null && klass != null){
            adapters = graph.get(type, klass);
            if(adapters == null){
                adapters = implicitGraph.get(type, klass);
            } if(adapters == null) {
                for(Class<?> iface : klass.getInterfaces()){
                    // todo check graph first
                    adapters = implicitGraph.get(type, iface);
                    if(adapters != null) break;
                }
                    
            }
            klass = klass.getSuperclass();
        }    
        
        if(adapters == null) 
            throw new ClassCastException("Did not find any transformer");
        Object t = context;
        for(AdapterFactory<Object,Object> adapter : adapters) {
            t = adapter.adapt(t);
        }
        
        return (T)t;
    }

    public <T> T adapt(Class<T> type, Object context, String name) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @SuppressWarnings("unchecked")
    public void register(AdapterFactory<?,?> adapter) {
        Query query = new Query<AdapterFactory<?,?>>(adapter);
        graph.insert(adapter.provides(), adapter.context(), query);
        implicitGraph.insert(adapter.provides(), adapter.context(), query);

        for(Class<?> to : adapter.provides().getInterfaces()) {
            for(Class<?> from : adapter.context().getInterfaces()) {
                implicitGraph.insert(to, from, query);
            }
        }
    }  
    
    /**
     * Set the adapters stored in this registry. 
     * This removes any adapters already registered.
     * use {@link AdapterRegistry#registerAdapters(java.lang.Iterable)} to add adapters. 
     * @param adapters represented by this registry
     * @since 0.1
     */
    public void setAdapters(Iterable<AdapterFactory<?,?>> adapters){
        graph = new TransitiveClosure<Class<?>, AdapterFactory<Object,Object>>();
        registerAdapters(adapters);
    }
     
}
